home *** CD-ROM | disk | FTP | other *** search
/ Multimedia Jumpstart / Multimedia Microsoft Jumpstart Version 1.1a (Microsoft).BIN / develpmt / sdk / vfw11.win / vfwdk / wavefile.c_ / wavefile.bin
Encoding:
Text File  |  1993-11-19  |  29.8 KB  |  1,096 lines

  1. /****************************************************************************
  2.  *
  3.  *  WAVEFILE.C
  4.  *
  5.  *  An implementation in C of an AVI File Handler to read standard windows
  6.  *  WAV files as if they were an AVI file with one audio stream.
  7.  *
  8.  ***************************************************************************/
  9. /**************************************************************************
  10.  *
  11.  *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  12.  *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  13.  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  14.  *  PURPOSE.
  15.  *
  16.  *  Copyright (c) 1992, 1993  Microsoft Corporation.  All Rights Reserved.
  17.  * 
  18.  **************************************************************************/
  19.  
  20. #include <windows.h>
  21. #include <windowsx.h>
  22. #include <mmsystem.h>
  23. #include <string.h>
  24. #include <stdlib.h>
  25. #include <malloc.h>
  26. #include <ctype.h>
  27. #include <avifile.h>
  28. #include "MulDiv32.h"
  29. #include "wavefile.h"
  30.  
  31. // Using Microsoft C7 and above, the default LibMain sets this variable up.
  32. extern HINSTANCE _hModule;
  33.  
  34. #define formtypeWAVE            mmioFOURCC('W', 'A', 'V', 'E')
  35. #define ckidWAVEFORMAT          mmioFOURCC('f', 'm', 't', ' ')
  36. #define ckidWAVEDATA            mmioFOURCC('d', 'a', 't', 'a')
  37.  
  38. //
  39. // Function prototypes and Vtbl for the Unknown interface
  40. //
  41. STDMETHODIMP WaveUnknownQueryInterface(LPUNKNOWN pu, REFIID iid, void FAR* FAR* ppv);
  42. STDMETHODIMP_(ULONG) WaveUnknownAddRef(LPUNKNOWN pu);
  43. STDMETHODIMP_(ULONG) WaveUnknownRelease(LPUNKNOWN pu);
  44.  
  45. IUnknownVtbl UnknownVtbl = {
  46.     WaveUnknownQueryInterface,
  47.     WaveUnknownAddRef,
  48.     WaveUnknownRelease
  49. };
  50.  
  51. //
  52. // Function prototypes and Vtbl for the AVIFile interface
  53. //
  54. STDMETHODIMP WaveFileQueryInterface(PAVIFILE pf, REFIID iid, void FAR* FAR* ppv);
  55. STDMETHODIMP_(ULONG) WaveFileAddRef(PAVIFILE pf);
  56. STDMETHODIMP_(ULONG) WaveFileRelease(PAVIFILE pf);
  57. STDMETHODIMP WaveFileOpen(PAVIFILE pf, LPCSTR szFile, UINT mode);
  58. STDMETHODIMP WaveFileInfo(PAVIFILE pf, AVIFILEINFO FAR * pfi, LONG lSize);
  59. STDMETHODIMP WaveFileGetStream(PAVIFILE pf, PAVISTREAM FAR * ppavi, DWORD fccType, LONG lParam);
  60. STDMETHODIMP WaveFileCreateStream(PAVIFILE pf, PAVISTREAM FAR *ppstream, AVISTREAMINFO FAR *psi);
  61. STDMETHODIMP WaveFileSave(PAVIFILE pf, LPCSTR szFile, AVICOMPRESSOPTIONS FAR *lpOptions, AVISAVECALLBACK lpfnCallback);
  62. STDMETHODIMP WaveFileWriteData(PAVIFILE pf, DWORD ckid, LPVOID lpData, LONG cbData);
  63. STDMETHODIMP WaveFileReadData(PAVIFILE pf, DWORD ckid, LPVOID lpData, LONG FAR *lpcbData);
  64. STDMETHODIMP WaveFileEndRecord(PAVIFILE pf);
  65. STDMETHODIMP WaveFileReserved(PAVIFILE pf);
  66.  
  67. IAVIFileVtbl FileVtbl = {
  68.     WaveFileQueryInterface,
  69.     WaveFileAddRef,
  70.     WaveFileRelease,
  71.     WaveFileOpen,
  72.     WaveFileInfo,
  73.     WaveFileGetStream,
  74.     WaveFileCreateStream,
  75.     WaveFileSave,
  76.     WaveFileWriteData,
  77.     WaveFileReadData,
  78.     WaveFileEndRecord,
  79.     WaveFileReserved,
  80.     WaveFileReserved,
  81.     WaveFileReserved,
  82.     WaveFileReserved,
  83.     WaveFileReserved
  84. };
  85.  
  86.  
  87. //
  88. // Function prototypes and Vtbl for the AVIStream interface
  89. //
  90. STDMETHODIMP WaveStreamQueryInterface(PAVISTREAM ps, REFIID riid, LPVOID FAR* ppvObj);
  91. STDMETHODIMP WaveStreamCreate(PAVISTREAM ps, LONG lParam1, LONG lParam2);
  92. STDMETHODIMP_(ULONG) WaveStreamAddRef(PAVISTREAM ps);
  93. STDMETHODIMP_(ULONG) WaveStreamRelease(PAVISTREAM ps);
  94. STDMETHODIMP WaveStreamInfo(PAVISTREAM ps, AVISTREAMINFO FAR * psi, LONG lSize);
  95. STDMETHODIMP_(LONG) WaveStreamFindSample(PAVISTREAM ps, LONG lPos, LONG lFlags);
  96. STDMETHODIMP WaveStreamReadFormat(PAVISTREAM ps, LONG lPos, LPVOID lpFormat, LONG FAR *lpcbFormat);
  97. STDMETHODIMP WaveStreamSetFormat(PAVISTREAM ps, LONG lPos, LPVOID lpFormat, LONG cbFormat);
  98. STDMETHODIMP WaveStreamRead(PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG FAR * plBytes,LONG FAR * plSamples);
  99. STDMETHODIMP WaveStreamWrite(PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpData, LONG cbData, DWORD dwFlags, LONG FAR *plSampWritten, LONG FAR *plBytesWritten);
  100. STDMETHODIMP WaveStreamDelete(PAVISTREAM ps, LONG lStart, LONG lSamples);
  101. STDMETHODIMP WaveStreamReadData(PAVISTREAM ps, DWORD fcc, LPVOID lp,LONG FAR *lpcb);
  102. STDMETHODIMP WaveStreamWriteData(PAVISTREAM ps, DWORD fcc, LPVOID lp,LONG cb);
  103. STDMETHODIMP WaveStreamReserved(PAVISTREAM ps);
  104.  
  105. IAVIStreamVtbl StreamVtbl = {
  106.     WaveStreamQueryInterface,
  107.     WaveStreamAddRef,
  108.     WaveStreamRelease,
  109.     WaveStreamCreate,
  110.     WaveStreamInfo,
  111.     WaveStreamFindSample,
  112.     WaveStreamReadFormat,
  113.     WaveStreamSetFormat,
  114.     WaveStreamRead,
  115.     WaveStreamWrite,
  116.     WaveStreamDelete,
  117.     WaveStreamReadData,
  118.     WaveStreamWriteData,
  119.     WaveStreamReserved,
  120.     WaveStreamReserved,
  121.     WaveStreamReserved,
  122.     WaveStreamReserved,
  123.     WaveStreamReserved
  124. };
  125.  
  126.  
  127. ///////////////////////////////////////////////////////////////////////////
  128. ///////////////////////////////////////////////////////////////////////////
  129. ///////////////////////////////////////////////////////////////////////////
  130.  
  131. /*    -    -    -    -    -    -    -    -    */
  132.  
  133. UINT    uUseCount;    // the reference count for our objects
  134. UINT    uLockCount;    // our lock count for LockServer
  135.  
  136. /*    -    -    -    -    -    -    -    -    */
  137.  
  138. //
  139. // Create a new instance.  Since this is a C implementation we have to 
  140. // allocate space for our structure ourselves.
  141. //
  142. HRESULT WaveFileCreate(
  143.     IUnknown FAR*    pUnknownOuter,
  144.     REFIID        riid,
  145.     void FAR* FAR*    ppv)
  146. {
  147.     IUnknown FAR*    pUnknown;
  148.     LPWAVESTUFF    pWaveStuff;
  149.     HRESULT    hresult;
  150.  
  151.     // Allocate space for our structure
  152.     pWaveStuff = (LPWAVESTUFF)GlobalAllocPtr(GMEM_MOVEABLE,
  153.         sizeof(WAVESTUFF));
  154.     if (!pWaveStuff)
  155.         return ResultFromScode(E_OUTOFMEMORY);
  156.  
  157.     // Initialize the Vtbls
  158.     pWaveStuff->AVIFile = &FileVtbl;
  159.     pWaveStuff->AVIStream = &StreamVtbl;
  160.     pWaveStuff->Unknown = &UnknownVtbl;
  161.  
  162.     // Set up our controlling object 
  163.     pUnknown = (IUnknown FAR *)&pWaveStuff->Unknown;
  164.     if (pUnknownOuter)
  165.         pWaveStuff->pUnknownOuter = pUnknownOuter;
  166.     else
  167.         pWaveStuff->pUnknownOuter =(IUnknown FAR *)&pWaveStuff->Unknown;
  168.  
  169.     // Initial the things in our structure
  170.     pWaveStuff->refs = 0;
  171.     pWaveStuff->hmmio = NULL;
  172.     pWaveStuff->lpFormat = NULL;
  173.     pWaveStuff->cbFormat = 0L;
  174.     pWaveStuff->fDirty = FALSE;
  175.     pWaveStuff->extra.lp = NULL;
  176.     pWaveStuff->extra.cb = 0L;
  177.  
  178.     // Call our Query interface to increment our ref count and get a
  179.     // pointer to our interface to return.
  180.     hresult = pUnknown->lpVtbl->QueryInterface(pUnknown, riid, ppv);
  181.  
  182.     if (FAILED(GetScode(hresult)))
  183.         GlobalFreePtr(pWaveStuff);
  184.     return hresult;
  185. }
  186.  
  187. /*    -    -    -    -    -    -    -    -    */
  188.  
  189. //
  190. // Query interface from all three interfaces comes here.  We support the
  191. // Unknown interface, AVIStream and AVIFile.
  192. //
  193. STDMETHODIMP WaveUnknownQueryInterface(
  194.     LPUNKNOWN    pu,
  195.     REFIID        iid,
  196.     void FAR* FAR*    ppv)
  197. {
  198.     // Get a pointer to our structure
  199.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_UNKNOWN(pu);
  200.  
  201.     if (IsEqualIID(iid, &IID_IUnknown))
  202.         *ppv = &pWaveStuff->Unknown;
  203.     else if (IsEqualIID(iid, &IID_IAVIFile))
  204.         *ppv = &pWaveStuff->AVIFile;
  205.     else if (IsEqualIID(iid, &IID_IAVIStream))
  206.         *ppv = &pWaveStuff->AVIStream;
  207.     else
  208.         return ResultFromScode(E_NOINTERFACE);
  209.     pu->lpVtbl->AddRef(pu);
  210.     return NULL;
  211. }
  212.  
  213. /*    -    -    -    -    -    -    -    -    */
  214.  
  215. //
  216. // Increase our reference count.  AddRef for all three interfaces comes here.
  217. //
  218. STDMETHODIMP_(ULONG) WaveUnknownAddRef(
  219.     LPUNKNOWN    pu)
  220. {
  221.     // Get a pointer to our structure
  222.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_UNKNOWN(pu);
  223.  
  224.     uUseCount++;
  225.     return ++pWaveStuff->refs;
  226. }
  227.  
  228. /*    -    -    -    -    -    -    -    -    */
  229.  
  230. //
  231. // Decrease our reference count.  Release for all three interfaces comes here.
  232. //
  233. STDMETHODIMP_(ULONG) WaveUnknownRelease(LPUNKNOWN pu)
  234. {
  235.     // Get a pointer to our structure
  236.     LPWAVESTUFF p = WAVESTUFF_FROM_UNKNOWN(pu);
  237.  
  238.     uUseCount--;
  239.  
  240.     //
  241.     // Ref count is zero.  Close the file.  If we've been writing to it, it's
  242.     // clean-up time!
  243.     //
  244.     if (!--p->refs) {
  245.     LONG lRet = AVIERR_OK;
  246.     
  247.     if (p->fDirty) {
  248.         MMCKINFO ckRIFF;
  249.         MMCKINFO ck;
  250.  
  251.         mmioSeek(p->hmmio, 0, SEEK_SET);
  252.  
  253.         /* create the output file RIFF chunk of form type 'WAVE' */
  254.         ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
  255.         ckRIFF.cksize = 0L;             // let MMIO figure out ck. size
  256.         if (mmioCreateChunk(p->hmmio, &ckRIFF, MMIO_CREATERIFF) != 0)
  257.             goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  258.  
  259.         ck.ckid = mmioFOURCC('f', 'm', 't', ' ');
  260.         ck.cksize = p->cbFormat;        // we know the size of this ck.
  261.         if (mmioCreateChunk(p->hmmio, &ck, 0) != 0)
  262.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  263.  
  264.         if (mmioWrite(p->hmmio, (HPSTR) p->lpFormat, p->cbFormat) != p->cbFormat)
  265.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  266.  
  267.         /* ascend out of the 'fmt' chunk, back into 'RIFF' chunk */
  268.         if (mmioAscend(p->hmmio, &ck, 0) != 0)
  269.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  270.  
  271.         // If there was extra stuff here, we need to fill it!
  272.         if (mmioSeek(p->hmmio, 0, SEEK_CUR) + 2 * sizeof(DWORD) !=
  273.             (LONG) p->ckData.dwDataOffset) {
  274.         /* create the 'data' chunk that holds the waveform samples */
  275.         ck.ckid = mmioFOURCC('J', 'U', 'N', 'K');
  276.         ck.cksize = 0;
  277.         if (mmioCreateChunk(p->hmmio, &ck, 0) != 0)
  278.             goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  279.  
  280.         mmioSeek(p->hmmio,
  281.              p->ckData.dwDataOffset - 2 * sizeof(DWORD),
  282.              SEEK_SET);
  283.  
  284.         if (mmioAscend(p->hmmio, &ck, 0) != 0)
  285.             goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  286.         }
  287.  
  288.         /* create the 'data' chunk that holds the waveform samples */
  289.         ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
  290.         ck.cksize = p->ckData.cksize;
  291.         if (mmioCreateChunk(p->hmmio, &ck, 0) != 0)
  292.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  293.  
  294.         mmioSeek(p->hmmio, p->ckData.cksize, SEEK_CUR);
  295.  
  296.         mmioAscend(p->hmmio, &ck, 0);
  297.  
  298.         if (p->extra.cb) {
  299.         if (mmioWrite(p->hmmio, (HPSTR) p->extra.lp, p->extra.cb) != p->extra.cb)
  300.             goto ERROR_CANNOT_WRITE;
  301.         }
  302.  
  303.         if (mmioAscend(p->hmmio, &ckRIFF, 0) != 0)
  304.         goto ERROR_CANNOT_WRITE;
  305.  
  306.         if (mmioFlush(p->hmmio, 0) != 0)
  307.         goto ERROR_CANNOT_WRITE;
  308.     }
  309.  
  310.  
  311.     goto success;
  312.  
  313.     ERROR_CANNOT_WRITE:
  314.     lRet = AVIERR_FILEWRITE;
  315.  
  316.     success:
  317.     if (p->hmmio)
  318.         mmioClose(p->hmmio, 0);
  319.  
  320.     if (p->lpFormat)
  321.         GlobalFreePtr(p->lpFormat);
  322.  
  323.     // Free the memory for our structure.
  324.     GlobalFreePtr(p);
  325.     return 0;
  326.     }
  327.     return p->refs;
  328. }
  329.  
  330.  
  331. //
  332. // Use our controlling object to call QueryInterface on Unknown
  333. //
  334. STDMETHODIMP WaveFileQueryInterface(
  335.     PAVIFILE    pf,
  336.     REFIID        iid,
  337.     void FAR* FAR*    ppv)
  338. {
  339.     // Get a pointer to our structure
  340.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_FILE(pf);
  341.  
  342.     return pWaveStuff->pUnknownOuter->lpVtbl->QueryInterface(
  343.         pWaveStuff->pUnknownOuter, iid, ppv);
  344. }
  345.  
  346. /*    -    -    -    -    -    -    -    -    */
  347.  
  348. //
  349. // Use our controlling object to call AddRef on Unknown
  350. //
  351. STDMETHODIMP_(ULONG) WaveFileAddRef(
  352.     PAVIFILE    pf)
  353. {
  354.     // Get a pointer to our structure
  355.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_FILE(pf);
  356.  
  357.     return pWaveStuff->pUnknownOuter->lpVtbl->AddRef(
  358.         pWaveStuff->pUnknownOuter);
  359. }
  360.  
  361. /*    -    -    -    -    -    -    -    -    */
  362.  
  363. //
  364. // Use our controlling object to call Release on Unknown
  365. //
  366. STDMETHODIMP_(ULONG) WaveFileRelease(
  367.     PAVIFILE    pf)
  368. {
  369.     // Get a pointer to our structure
  370.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_FILE(pf);
  371.  
  372.     return pWaveStuff->pUnknownOuter->lpVtbl->Release(
  373.         pWaveStuff->pUnknownOuter);
  374. }
  375.  
  376. /*    -    -    -    -    -    -    -    -    */
  377.  
  378.  
  379.  
  380. //
  381. // Use our controlling object to call QueryInterface on Unknown
  382. //
  383. STDMETHODIMP WaveStreamQueryInterface(
  384.     PAVISTREAM    ps,
  385.     REFIID        iid,
  386.     void FAR* FAR*    ppv)
  387. {
  388.     // Get a pointer to our structure
  389.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_STREAM(ps);
  390.  
  391.     return pWaveStuff->pUnknownOuter->lpVtbl->QueryInterface(
  392.         pWaveStuff->pUnknownOuter, iid, ppv);
  393. }
  394.  
  395. /*    -    -    -    -    -    -    -    -    */
  396.  
  397. //
  398. // Use our controlling object to call AddRef on Unknown
  399. //
  400. STDMETHODIMP_(ULONG) WaveStreamAddRef(
  401.     PAVISTREAM    ps)
  402. {
  403.     // Get a pointer to our structure
  404.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_STREAM(ps);
  405.  
  406.     return pWaveStuff->pUnknownOuter->lpVtbl->AddRef(
  407.         pWaveStuff->pUnknownOuter);
  408. }
  409.  
  410. /*    -    -    -    -    -    -    -    -    */
  411.  
  412. //
  413. // Use our controlling object to call Release on Unknown
  414. //
  415. STDMETHODIMP_(ULONG) WaveStreamRelease(
  416.     PAVISTREAM    ps)
  417. {
  418.     // Get a pointer to our structure
  419.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_STREAM(ps);
  420.  
  421.     return pWaveStuff->pUnknownOuter->lpVtbl->Release(
  422.         pWaveStuff->pUnknownOuter);
  423. }
  424.  
  425. /*    -    -    -    -    -    -    -    -    */
  426.  
  427. #define SLASH(c)     ((c) == '/' || (c) == '\\')
  428.  
  429. /*--------------------------------------------------------------+
  430. | FileName  - return a pointer to the filename part of szPath   |
  431. |             with no preceding path.                           |
  432. +--------------------------------------------------------------*/
  433. LPSTR FAR FileName(LPCSTR lszPath)
  434. {
  435.     LPCSTR   lszCur;
  436.  
  437.     for (lszCur = lszPath + lstrlen(lszPath); lszCur > lszPath && !SLASH(*lszCur) && *lszCur != ':';)
  438.     lszCur = AnsiPrev(lszPath, lszCur);
  439.     if (lszCur == lszPath)
  440.     return (LPSTR)lszCur;
  441.     else
  442.     return (LPSTR)(lszCur + 1);
  443. }
  444.  
  445. /*    -    -    -    -    -    -    -    -    */
  446.  
  447. //
  448. // The Open Method for our File interface - Open a WAVE file
  449. //
  450. STDMETHODIMP WaveFileOpen(PAVIFILE pf, LPCSTR szFile, UINT mode)
  451. {
  452.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  453.     UINT    ui;
  454.     char    ach[80];
  455.  
  456.     // !!! Assumptions about the AVIFILE.DLL (which calls us):
  457.     // We will only see READWRITE mode, never only WRITE mode.
  458.  
  459.     // force the share flags to the 'correct' values
  460.     // If we're writing, use Exclusive mode.  If we're reading, use DenyWrite.
  461.     if (mode & OF_READWRITE) {
  462.     mode = (mode & ~(MMIO_SHAREMODE)) | OF_SHARE_EXCLUSIVE;
  463.     } else {
  464.     mode = (mode & ~(MMIO_SHAREMODE)) | OF_SHARE_DENY_WRITE;
  465.     }
  466.  
  467.     //
  468.     // try to open the actual file, first with share, then without.  
  469.     // You may need to use specific flags in order to open a file
  470.     // that's already open by somebody else.
  471.     //
  472.  
  473.     // If the first attempt fails, no system error box, please.
  474.     ui = SetErrorMode(SEM_NOOPENFILEERRORBOX);
  475.     p->hmmio = mmioOpen((LPSTR) szFile, NULL, MMIO_ALLOCBUF | mode);
  476.     if (!p->hmmio && ((mode & MMIO_RWMODE) == OF_READ)) {
  477.     // if the open fails, try again without the share flags.
  478.     mode &= ~(MMIO_SHAREMODE);
  479.     p->hmmio = mmioOpen((LPSTR) szFile, NULL, MMIO_ALLOCBUF | mode);
  480.     }
  481.     SetErrorMode(ui);
  482.     
  483.     //
  484.     // Now set up our structure
  485.     //
  486.  
  487.     p->mode = mode;
  488.     
  489.     if (!p->hmmio)
  490.         goto error;
  491.  
  492.     _fmemset(&p->avistream, 0, sizeof(p->avistream));
  493.  
  494. // If this is defined, we pretend that the data is at FPSHACK "frames"
  495. // per second in the main header, otherwise we use the sample
  496. // rate of the audio, which looks somewhat strange in MPlayer.
  497. #define FPSHACK        1000
  498.     
  499.     _fmemset(&p->avihdr, 0, sizeof(p->avihdr));
  500.  
  501. #ifdef FPSHACK
  502.     //
  503.     // Initialize our AVIFILEHEADER
  504.     //
  505.     p->avihdr.dwRate = FPSHACK;
  506.     p->avihdr.dwScale = 1;
  507. #endif
  508.     
  509.     p->avihdr.dwStreams = 1;
  510.     LoadString(_hModule, IDS_FILETYPE, p->avihdr.szFileType,
  511.     sizeof(p->avihdr.szFileType));
  512.     
  513.     //
  514.     // Initialize our AVISTREAMHEADER
  515.     //
  516.     LoadString(_hModule, IDS_STREAMNAME, ach, sizeof(ach));
  517.     wsprintf(p->avistream.szName, ach, FileName(szFile));
  518.  
  519.     if (mode & OF_CREATE) {    // Brand new file
  520.     p->avistream.fccType = streamtypeAUDIO;
  521.     p->avistream.fccHandler = 0;
  522.     p->avistream.dwFlags = 0;
  523.     p->avistream.wPriority = 0;
  524.     p->avistream.wLanguage = 0;
  525.     p->avistream.dwInitialFrames = 0;       
  526.     p->avistream.dwScale = 0;
  527.     p->avistream.dwRate = 0;
  528.     p->avistream.dwStart = 0;           
  529.     p->avistream.dwLength = 0;
  530.     p->avistream.dwSuggestedBufferSize = 0;
  531.     p->avistream.dwSampleSize = 0;
  532.     
  533.     p->fDirty = TRUE;
  534.     
  535.     } else {        // read the existing file to get info
  536.  
  537.     MMCKINFO        ck;
  538.     MMCKINFO        ckRIFF;
  539.     /* Read RIFF chunk */
  540.     if (mmioDescend(p->hmmio, &ckRIFF, NULL, 0) != 0)
  541.         goto error;
  542.  
  543.     if (ckRIFF.ckid != FOURCC_RIFF)
  544.         goto error;
  545.  
  546.     if (ckRIFF.fccType != formtypeWAVE)
  547.         goto error;
  548.  
  549.     /* Read WAVE format chunk */
  550.     ck.ckid = ckidWAVEFORMAT;
  551.     if (FindChunkAndKeepExtras(&p->extra, p->hmmio, &ck, &ckRIFF, MMIO_FINDCHUNK))
  552.         goto error;
  553.  
  554.     p->cbFormat = ck.cksize;
  555.     p->lpFormat = (LPWAVEFORMAT) GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize);
  556.  
  557.     if (p->lpFormat == NULL)
  558.         goto error;
  559.  
  560.     if (mmioRead(p->hmmio,
  561.              (HPSTR) p->lpFormat,
  562.              (LONG)ck.cksize) != (LONG)ck.cksize)
  563.         goto error;
  564.  
  565.  
  566.     /* Ascend out of stream header */
  567.     if (mmioAscend(p->hmmio, &ck, 0) != 0)
  568.         goto error;
  569.  
  570.     /* Find big data chunk */
  571.     p->ckData.ckid = ckidWAVEDATA;
  572.     if (FindChunkAndKeepExtras(&p->extra, p->hmmio, &p->ckData, &ckRIFF, MMIO_FINDCHUNK))
  573.         goto error;
  574.  
  575.     p->fDirty = FALSE;
  576.     
  577.     p->avistream.fccType = streamtypeAUDIO;
  578.     p->avistream.fccHandler = 0;
  579.     p->avistream.dwFlags = 0;
  580.     p->avistream.wPriority = 0;           
  581.     p->avistream.wLanguage = 0;           
  582.     p->avistream.dwInitialFrames = 0;       
  583.     p->avistream.dwScale = p->lpFormat->nBlockAlign;           
  584.     p->avistream.dwRate = p->lpFormat->nAvgBytesPerSec;
  585.     p->avistream.dwStart = 0;           
  586.     p->avistream.dwLength = p->ckData.cksize / p->lpFormat->nBlockAlign; 
  587.     p->avistream.dwSuggestedBufferSize = 0;
  588.     p->avistream.dwSampleSize = p->lpFormat->nBlockAlign;
  589.     
  590. #ifdef FPSHACK
  591.     p->avihdr.dwLength =
  592.                 MulDiv32(p->avistream.dwLength,
  593.                   p->avistream.dwScale * FPSHACK,
  594.                   p->avistream.dwRate);
  595. #else
  596.     p->avihdr.dwScale = 1;
  597.     p->avihdr.dwRate = p->lpFormat->nSamplesPerSec;
  598.     p->avihdr.dwLength =
  599.                 MulDiv32(p->ckData.cksize, p->lpFormat->nSamplesPerSec,
  600.              p->lpFormat->nAvgBytesPerSec);
  601.              
  602. #endif
  603.  
  604.  
  605.     mmioAscend(p->hmmio, &p->ckData, 0);
  606.     
  607.     // Read extra data at end of file....
  608.     if (FindChunkAndKeepExtras(&p->extra, p->hmmio, &ckRIFF, &ck, 0) !=
  609.                         AVIERR_OK)
  610.         goto error;
  611.     }
  612.     
  613.     //
  614.     // all done return success.
  615.     //
  616.     return ResultFromScode(0); // success
  617.     
  618. error:
  619.     return ResultFromScode(AVIERR_FILEREAD);
  620. }
  621.  
  622.  
  623. //
  624. // Get a stream from the file... Each WAVE file has exactly 1 audio stream.
  625. //
  626. STDMETHODIMP WaveFileGetStream(PAVIFILE pf,
  627.                 PAVISTREAM FAR * ppavi,
  628.                 DWORD fccType,
  629.                 LONG lParam)
  630. {
  631.     int             iStreamWant;
  632.     // Get a pointer to our structure
  633.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  634.  
  635.     iStreamWant = (int)lParam;
  636.  
  637.     if (p->lpFormat == NULL)
  638.     return ResultFromScode(AVIERR_BADPARAM);
  639.     
  640.     // We only support one stream
  641.     if (iStreamWant != 0)
  642.         return ResultFromScode(AVIERR_BADPARAM);
  643.  
  644.     // We only support audio streams
  645.     if (fccType && fccType != streamtypeAUDIO)
  646.     return ResultFromScode(AVIERR_BADPARAM);
  647.  
  648.     // increase the reference count
  649.     p->AVIStream->AddRef((PAVISTREAM)&p->AVIStream);
  650.     
  651.     // Return a pointer to our stream Vtbl
  652.     *ppavi = (PAVISTREAM) &(p->AVIStream);
  653.     return ResultFromScode(AVIERR_OK);
  654. }
  655.  
  656. //
  657. // We don't support the Save Method of the File Interface (We don't save)
  658. //
  659. STDMETHODIMP WaveFileSave(PAVIFILE pf,
  660.                    LPCSTR szFile,
  661.                    AVICOMPRESSOPTIONS FAR *lpOptions,
  662.                    AVISAVECALLBACK lpfnCallback)
  663. {
  664.     return ResultFromScode(AVIERR_UNSUPPORTED);
  665. }
  666.  
  667. //
  668. // Method to create a stream in a WAVE file.  We only support this for blank
  669. // WAVE files.
  670. //
  671. STDMETHODIMP WaveFileCreateStream(PAVIFILE pf,
  672.             PAVISTREAM FAR *ppstream,
  673.                 AVISTREAMINFO FAR *psi)
  674. {
  675.     // Get a pointer to our structure
  676.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  677.  
  678.     // We can't add a second stream to a file
  679.     if (p->lpFormat)
  680.     return ResultFromScode(AVIERR_UNSUPPORTED);
  681.  
  682.     // We only like audio....
  683.     if (psi->fccType != streamtypeAUDIO)
  684.     return ResultFromScode(AVIERR_UNSUPPORTED);
  685.     
  686.     // Increase our reference count.
  687.     p->AVIStream->AddRef((PAVISTREAM)&p->AVIStream);
  688.  
  689.     p->cbFormat = 0;
  690.     p->lpFormat = NULL;
  691.  
  692.     // Return a pointer to our stream Vtbl.
  693.     *ppstream = (PAVISTREAM) &(p->AVIStream);
  694.     
  695.     return ResultFromScode(AVIERR_OK);
  696. }
  697.  
  698. //
  699. // The WriteData Method of the File interface
  700. //
  701. STDMETHODIMP WaveFileWriteData(PAVIFILE pf,
  702.         DWORD ckid,
  703.         LPVOID lpData,
  704.         LONG cbData)
  705. {
  706.     // Get a pointer to our structure
  707.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  708.  
  709.     // Write the data in the Wave File.
  710.     return  ResultFromScode(WriteExtra(&p->extra, ckid, lpData, cbData));
  711. }
  712.  
  713. //
  714. // The ReadData Method of the File interface
  715. //
  716. STDMETHODIMP WaveFileReadData(PAVIFILE pf,
  717.         DWORD ckid,
  718.         LPVOID lpData,
  719.         LONG FAR *lpcbData)
  720. {
  721.     // Get a pointer to our structure
  722.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  723.  
  724.     // Read the data from the file
  725.     return ResultFromScode(ReadExtra(&p->extra, ckid, lpData, lpcbData));
  726. }
  727.  
  728. //
  729. // The EndRecord Method of the File interface.. this doesn't need to do
  730. // anything.. (no concept of interleaving or packaging streams)
  731. //
  732. STDMETHODIMP WaveFileEndRecord(PAVIFILE pf)
  733. {
  734.     return ResultFromScode(AVIERR_OK);
  735. }
  736.  
  737.  
  738. //
  739. // The Info Method of the File interface
  740. //
  741. STDMETHODIMP WaveFileInfo(PAVIFILE pf,
  742.           AVIFILEINFO FAR * pfi,
  743.           LONG lSize)
  744. {
  745.     // Get a pointer to our structure
  746.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  747.  
  748.     // Return an AVIFILEHEADER.
  749.     hmemcpy(pfi, &p->avihdr, min(lSize, sizeof(p->avihdr)));
  750.     return 0;
  751. }
  752.  
  753.  
  754.  
  755. //
  756. // The Create Method of the Stream interface. We can't create streams that
  757. // aren't attached to the file.
  758. //
  759. STDMETHODIMP WaveStreamCreate(
  760.     PAVISTREAM    ps,
  761.     LONG lParam1, LONG lParam2)
  762. {
  763.     return ResultFromScode(AVIERR_UNSUPPORTED);
  764. }
  765.  
  766.  
  767. //
  768. // The FindSample Method of the Stream interface
  769. //
  770. STDMETHODIMP_(LONG) WaveStreamFindSample(
  771.     PAVISTREAM    ps,
  772.     LONG lPos, LONG lFlags)
  773. {
  774.     if (lFlags & FIND_FORMAT) {
  775.         if ((lFlags & FIND_NEXT) && lPos > 0)
  776.             return -1;
  777.         else
  778.             return 0;
  779.     }
  780.  
  781.     return lPos;    
  782. }
  783.  
  784.  
  785. //
  786. // The ReadFormat Method of the Stream interface
  787. //
  788. STDMETHODIMP WaveStreamReadFormat(
  789.     PAVISTREAM    ps,
  790.     LONG lPos, LPVOID lpFormat, LONG FAR *lpcbFormat)
  791. {
  792.     // Get a pointer to our structure
  793.     LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);
  794.  
  795.     // No buffer to fill in, this means return the size needed.
  796.     if (lpFormat == NULL || *lpcbFormat == 0) {
  797.         *lpcbFormat = p->cbFormat;
  798.     return 0;
  799.     }
  800.  
  801.     // Give them the WAVE format.
  802.     hmemcpy(lpFormat, p->lpFormat, min(*lpcbFormat, p->cbFormat));
  803.  
  804.     // Our buffer is too small
  805.     if (*lpcbFormat < p->cbFormat)
  806.         return ResultFromScode(AVIERR_BUFFERTOOSMALL);
  807.  
  808.     *lpcbFormat = p->cbFormat;
  809.  
  810.     return 0;
  811. }
  812.  
  813. //
  814. // The Info Method of the Stream interface
  815. //
  816. STDMETHODIMP WaveStreamInfo(
  817.     PAVISTREAM    ps,
  818.     AVISTREAMINFO FAR * psi,
  819.     LONG lSize)
  820. {
  821.     // Get a pointer to our structure
  822.     LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);
  823.  
  824.     // give them an AVISTREAMINFO
  825.     hmemcpy(psi, &p->avistream, min(lSize, sizeof(p->avistream)));
  826.     return 0;
  827. }
  828.  
  829.  
  830. ///////////////////////////////////////////////////////////////////////////
  831. ///////////////////////////////////////////////////////////////////////////
  832.  
  833. /*
  834.         invalid lPos return error
  835.  
  836.         if lPos + lSamples is invalid trim lSamples to fit.
  837.  
  838.         lpBuffer == NULL
  839.  
  840.             cbBuffer == 0 && lSamples > 0
  841.                 return size of lSamples sample.
  842.             else
  843.                 return the exactly the number of bytes and sample
  844.                 you would have read if lpBuffer was not zero.
  845.  
  846.             NOTE return means fill in *plBytes and *plSamples.
  847.  
  848.         lpBuffer != NULL
  849.  
  850.             lSamples == -1      read convenient amount (just fill buffer)
  851.             lSamples == 0       fill buffer with as many samples that will fit.
  852.             lSamples >  0       read lSamples (or as much will fit in cbBuffer)
  853.  
  854.             fill in *plBytes   with bytes actualy read
  855.             fill in *plSamples with samples actualy read
  856.  
  857. */
  858.  
  859. //
  860. // The Read Method for the Stream Interface - Read some wave data
  861. STDMETHODIMP WaveStreamRead(
  862.         PAVISTREAM ps,
  863.         LONG       lStart,
  864.                 LONG       lSamples,
  865.                 LPVOID     lpBuffer,
  866.                 LONG       cbBuffer,
  867.                 LONG FAR * plBytes,
  868.                 LONG FAR * plSamples)
  869. {
  870.     // Get a pointer to our structure
  871.     LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);
  872.  
  873.     LONG          lSampleSize;
  874.     LONG          lSeek;
  875.     LONG          lRead;
  876.  
  877.     // Invalid position
  878.     if (lStart < 0 || lStart > (LONG) p->avistream.dwLength) {
  879. ack:
  880.     if (plBytes)
  881.         *plBytes = 0;
  882.     if (plSamples)
  883.         *plSamples = 0;
  884.     return 0;
  885.     }
  886.     
  887.     // Can't read quite this much data
  888.     if (lSamples + lStart > (LONG) p->avistream.dwLength)
  889.     lSamples = p->avistream.dwLength - lStart;
  890.     
  891.     lSampleSize = p->avistream.dwSampleSize;
  892.  
  893.     // We have fixed-length samples
  894.  
  895.     if (lpBuffer == NULL) {
  896.         if (cbBuffer > 0 && lSamples > 0)
  897.         // Trim how many samples we'd really be able to read
  898.         lSamples = min(lSamples, cbBuffer / lSampleSize);
  899.         else if (lSamples <= 0)
  900.         // Use as many as will fit
  901.         lSamples = cbBuffer / lSampleSize;
  902.     } else {
  903.         if (lSamples > 0)
  904.         // Trim how many samples we'd really be able to read
  905.         lSamples = min(lSamples, cbBuffer / lSampleSize);
  906.         else
  907.         // Use as many as will fit
  908.         lSamples = cbBuffer / lSampleSize;
  909.     }
  910.  
  911.     //
  912.     // a NULL buffer means return the size buffer needed to read
  913.     // the given sample.
  914.     //
  915.     if (lpBuffer == NULL || cbBuffer == 0) {
  916.     if (plBytes)
  917.         *plBytes =  lSamples * lSampleSize;;
  918.     if (plSamples)
  919.         *plSamples = lSamples;
  920.     return 0;
  921.     }
  922.  
  923.     // Buffer too small!
  924.     if (cbBuffer < lSampleSize)
  925.         goto ack;
  926.  
  927.     // Seek and read
  928.  
  929.     cbBuffer = lSamples * lSampleSize;
  930.  
  931.     lSeek = p->ckData.dwDataOffset + lSampleSize * lStart;
  932.     lRead = lSamples * lSampleSize;
  933.     
  934.     if (mmioSeek(p->hmmio, lSeek, SEEK_SET) != lSeek)
  935.     goto ack;
  936.  
  937.     if (mmioRead(p->hmmio, (HPSTR) lpBuffer, lRead) != lRead)
  938.     goto ack;
  939.     
  940.     //
  941.     // success return number of bytes and number of samples read
  942.     //
  943.     if (plBytes)
  944.         *plBytes = lRead;
  945.  
  946.     if (plSamples)
  947.         *plSamples = lSamples;
  948.  
  949.     return ResultFromScode(AVIERR_OK);
  950. }
  951.  
  952.  
  953. //
  954. // The SetFormat Method of the Stream interface    - called on an empty WAVE file
  955. // before writing data to it.
  956. //
  957. STDMETHODIMP WaveStreamSetFormat(
  958.     PAVISTREAM ps, LONG lPos,LPVOID lpFormat,LONG cbFormat)
  959. {
  960.     // Get a pointer to our structure
  961.     LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);
  962.  
  963.     // We can only do this to an empty wave file
  964.     if (p->lpFormat) {
  965.     if (cbFormat != p->cbFormat ||
  966.         _fmemcmp(lpFormat, p->lpFormat, (int) cbFormat))
  967.         return ResultFromScode(AVIERR_UNSUPPORTED);
  968.     
  969.     return NOERROR;
  970.     }
  971.     
  972.     // Go ahead and set the format!
  973.  
  974.     p->cbFormat = cbFormat;
  975.     p->lpFormat = (LPWAVEFORMAT) GlobalAllocPtr(GMEM_MOVEABLE, cbFormat);
  976.  
  977.     if (p->lpFormat == NULL)
  978.     return ResultFromScode(AVIERR_MEMORY);
  979.  
  980.     hmemcpy(p->lpFormat, lpFormat, cbFormat);
  981.  
  982.     p->ckData.dwDataOffset = cbFormat + 7 * sizeof(DWORD);
  983.     p->ckData.cksize = 0;
  984.     p->avistream.dwScale = p->lpFormat->nBlockAlign;           
  985.     p->avistream.dwRate = p->lpFormat->nAvgBytesPerSec;
  986.     p->avistream.dwLength = 0;
  987.     p->avistream.dwSampleSize = p->lpFormat->nBlockAlign;
  988.  
  989. #ifndef FPSHACK
  990.     p->avihdr.dwScale = 1;
  991.     p->avihdr.dwRate = p->lpFormat->nSamplesPerSec;
  992. #endif
  993.     return ResultFromScode(AVIERR_OK);
  994. }
  995.  
  996. //
  997. // The Write Method of the Stream interface - write some wave data
  998. //
  999. STDMETHODIMP WaveStreamWrite(             PAVISTREAM ps,
  1000.                          LONG lStart,
  1001.                          LONG lSamples,
  1002.                          LPVOID lpData,
  1003.                          LONG cbData,
  1004.                          DWORD dwFlags,
  1005.                          LONG FAR *plSampWritten,
  1006.                          LONG FAR *plBytesWritten)
  1007. {
  1008.     // Get a pointer to our structure
  1009.     LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);
  1010.  
  1011.     if ((p->mode & (OF_WRITE | OF_READWRITE)) == 0)
  1012.     return ResultFromScode(AVIERR_READONLY);
  1013.  
  1014.     // < 0 means "at end"
  1015.     if (lStart < 0)
  1016.     // !!!
  1017.     lStart = p->avistream.dwStart + p->avistream.dwLength;
  1018.  
  1019. #if 0 // !!! don't check for too long - why not?
  1020.     if (lStart > (LONG) (p->avistream.dwStart + p->avistream.dwLength))
  1021.     return ResultFromScode(AVIERR_BADPARAM);
  1022. #endif
  1023.  
  1024.     p->fDirty = TRUE;
  1025.  
  1026.     mmioSeek(p->hmmio,
  1027.          p->ckData.dwDataOffset +
  1028.              lStart * p->avistream.dwSampleSize,
  1029.          SEEK_SET);
  1030.  
  1031.     if (mmioWrite(p->hmmio, (HPSTR) lpData, cbData) != cbData)
  1032.     return ResultFromScode(AVIERR_FILEWRITE);
  1033.  
  1034.     p->avistream.dwLength = max((LONG) p->avistream.dwLength,
  1035.                   lStart + lSamples);
  1036.  
  1037.     p->ckData.cksize = max(p->ckData.cksize,
  1038.                lStart * p->avistream.dwSampleSize + cbData);
  1039.  
  1040. #ifdef FPSHACK
  1041.     p->avihdr.dwLength =
  1042.                 MulDiv32(p->avistream.dwLength * FPSHACK,
  1043.                   p->avistream.dwScale,
  1044.                   p->avistream.dwRate);
  1045. #else
  1046.     p->avihdr.dwLength =
  1047.                 MulDiv32(p->ckData.cksize, p->lpFormat->nSamplesPerSec,
  1048.              p->lpFormat->nAvgBytesPerSec);
  1049.              
  1050. #endif
  1051.  
  1052.     
  1053.     if (plSampWritten)
  1054.     *plSampWritten = lSamples;
  1055.  
  1056.     if (plBytesWritten)
  1057.     *plBytesWritten = cbData;
  1058.     
  1059.     return ResultFromScode(AVIERR_OK);
  1060. }
  1061.  
  1062. //
  1063. // The Delete Method of the Stream interface - we don't cut from wave files
  1064. //
  1065. STDMETHODIMP WaveStreamDelete(PAVISTREAM ps, LONG lStart,LONG lSamples)
  1066. {
  1067.     return ResultFromScode(AVIERR_UNSUPPORTED);
  1068. }
  1069.  
  1070.  
  1071. //
  1072. // We also don't support ReadData and WriteData for the Stream Interface
  1073. //
  1074.  
  1075. STDMETHODIMP WaveStreamReadData(PAVISTREAM ps,
  1076.         DWORD fcc, LPVOID lp, LONG FAR *lpcb)
  1077. {
  1078.     return ResultFromScode(AVIERR_UNSUPPORTED);
  1079. }
  1080.  
  1081. STDMETHODIMP WaveStreamWriteData(PAVISTREAM ps, DWORD fcc, LPVOID lp, LONG cb)
  1082. {
  1083.     return ResultFromScode(AVIERR_UNSUPPORTED);
  1084. }
  1085.  
  1086.  
  1087. STDMETHODIMP WaveFileReserved(PAVIFILE pf)
  1088. {
  1089.     return ResultFromScode(AVIERR_UNSUPPORTED);
  1090. }
  1091.  
  1092. STDMETHODIMP WaveStreamReserved(PAVISTREAM ps)
  1093. {
  1094.     return ResultFromScode(AVIERR_UNSUPPORTED);
  1095. }
  1096.